package com.hk.shop.base.dao;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Types;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import javax.persistence.LockModeType;

import org.apache.commons.lang.StringUtils;
import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.SQLQuery;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.hibernate.jdbc.ReturningWork;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.Assert;

import com.hk.shop.page.Page;
import com.hk.shop.page.PageFilter;
import com.hk.shop.page.PageFilter.Operator;
import com.hk.shop.page.PageInfo;
import com.hk.shop.page.PageOrder;
import com.hk.shop.page.PageOrder.Direction;

public abstract class BaseDaoImpl<T, ID extends Serializable> implements BaseDao<T, ID> {

	private Class<T> clazz;

	@Autowired
	private SessionFactory sessionFactory;

	@SuppressWarnings("unchecked")
	public BaseDaoImpl() {
		try {
			Type genType = getClass().getGenericSuperclass();
			Type[] params = ((ParameterizedType) genType).getActualTypeArguments();
			clazz = (Class<T>) params[0];
		} catch (Exception e) {
			clazz = null;
		}
	}

	public Session getCurrentSession() {
		return sessionFactory.getCurrentSession();
	}

	@Override
	public T insert(T t) {
		getCurrentSession().save(t);
		return t;
	}

	@Override
	public T saveOrUpdate(T t) {
		getCurrentSession().saveOrUpdate(t);
		return t;
	}

	@Override
	public void delete(T t) {
		getCurrentSession().delete(t);
	}

	@Override
	public void update(T t) {
		getCurrentSession().update(t);
	}

	@SuppressWarnings("unchecked")
	@Override
	public T get(ID id) {
		return (T) getCurrentSession().get(clazz, id);
	}

	@SuppressWarnings("unchecked")
	@Override
	public T findUniqueByProperty(String propertyName, Object value) {
		Assert.hasText(propertyName);
		Criteria criteria = getCurrentSession().createCriteria(clazz);
		criteria.add(Restrictions.eq(propertyName, value));
		return (T) criteria.uniqueResult();
	}

	@SuppressWarnings("unchecked")
	@Override
	public T load(long id) {
		return (T) getCurrentSession().load(clazz, id);
	}

	/* 该方法即把 生成和更新写到一个方法里 */
	@Override
	public T save(T t) {
		Assert.notNull(t, "entity不能为空");
		getCurrentSession().saveOrUpdate(t);
		return t;
	}

	// 动态分页
	@Override
	public Page<T> findPage(PageInfo pageInfo) {
		Criteria criteria = getCurrentSession().createCriteria(clazz);
		return findPage(criteria, pageInfo);
	}

	// 分页方法
	@SuppressWarnings("unchecked")
	@Override
	public Page<T> findPage(PageInfo pageInfo, String hql) {
		String countSql = "select count(*) " + hql;
		Query query1 = getCurrentSession().createQuery(countSql);
		Long total = ((Long) query1.uniqueResult());
		List<T> list = new ArrayList<T>();
		if (total > 0) {
			Query query2 = getCurrentSession().createQuery(hql);
			query2.setFirstResult((pageInfo.getPageNumber() - 1) * (pageInfo.getPageSize()));
			query2.setMaxResults(pageInfo.getPageSize());
			list = query2.list();

		}
		return new Page<T>(list, total, pageInfo);
	}

	// 分页方法
	@SuppressWarnings("unchecked")
	public Page<T> findPage(Criteria criteria, PageInfo pageInfo) {

		if (pageInfo == null) {
			pageInfo = new PageInfo();
		}
		addRestrictions(criteria, pageInfo.getFilters());
		addOrders(criteria, pageInfo.getOrders());

		long totalRows = count(criteria, null); // 此处传值为null是因为上两步已经把filter添加到criteria中去了
		int totalPages = (int) Math.ceil((double) totalRows / (double) pageInfo.getPageSize());
		if (totalPages < pageInfo.getPageNumber())
			pageInfo.setPageNumber(totalPages); // 此处考虑到页面传的pageNum大于实际值

		criteria.setProjection(null); // 注销count(*)函数
		criteria.setFirstResult((pageInfo.getPageNumber() - 1) * (pageInfo.getPageSize()));
		criteria.setMaxResults(pageInfo.getPageSize());
		List<T> list = criteria.list();
		return new Page<T>(list, totalRows, pageInfo);

	}

	@Override
	public Long queryTotalNum(PageInfo pageInfo) {
		Criteria criteria = getCurrentSession().createCriteria(clazz);
		if (pageInfo != null) {
			addRestrictions(criteria, pageInfo.getFilters());
			addOrders(criteria, pageInfo.getOrders());
		}
		return count(criteria, null);
	}

	// 获取最大数量, 如果order by和group by 没有意义
	protected Long count(Criteria criteria, List<PageFilter> filters) {
		// Projections 用来调用sql函数
		// Projections.sum(propertyName).min(propertyName).max(propertyName).distinct(proj).
		// count(propertyName).avg(propertyName),
		Assert.notNull(criteria);

		addRestrictions(criteria, filters);
		criteria.setProjection(Projections.rowCount());

		return (Long) criteria.uniqueResult();

	}

	@Override
	public long count(PageFilter... filters) {

		/*
		 * CriteriaBuilder criteriaBuilder = entityManager.getCriteriaBuilder();
		 * CriteriaQuery<T> criteriaQuery =
		 * criteriaBuilder.createQuery(entityClass);
		 * criteriaQuery.select(criteriaQuery.from(entityClass));
		 */

		Criteria criteria = getCurrentSession().createCriteria(clazz);
		return count(criteria, filters != null ? Arrays.asList(filters) : null);
	}
	
	
	public long count(String hql, Object[] parms) {
		Query query = sessionFactory.getCurrentSession().createQuery(hql);
		if (parms != null) {
			for (int i = 0; i < parms.length; i++) {
				query.setParameter(i, parms[i]);
			}
		}
		List cc = query.list();
		return (Long) cc.get(0);
	}

	/**
	 * 添加where条件
	 * 
	 * @param criteria
	 * @param filters
	 */
	private void addRestrictions(Criteria criteria, List<PageFilter> filters) {
		if (criteria == null || filters == null || filters.isEmpty()) {
			return;
		}
		for (PageFilter filter : filters) {
			if (filter == null || StringUtils.isEmpty(filter.getProperty())) {
				continue;
			}
			if (filter.getOperator() == Operator.eq && filter.getValue() != null) {
				if (filter.getIgnoreCase() != null && filter.getIgnoreCase() && filter.getValue() instanceof String) {
					// restrictions = criteriaBuilder.and(restrictions,
					// criteriaBuilder.equal(criteriaBuilder.lower(root.<String>
					// get(filter.getProperty())), ((String)
					// filter.getValue()).toLowerCase()));
					criteria.add(Restrictions.eq(filter.getProperty().toLowerCase(),
							Integer.valueOf(filter.getValue().toString())));
				} else {
					criteria.add(Restrictions.eq(filter.getProperty(), Integer.valueOf(filter.getValue().toString())));
				}
			} else if (filter.getOperator() == Operator.ne && filter.getValue() != null) {
				if (filter.getIgnoreCase() != null && filter.getValue() instanceof String) { //
					criteria.add(Restrictions.ne(filter.getProperty().toLowerCase(),
							Integer.valueOf(filter.getValue().toString())));
				} else {
					criteria.add(Restrictions.ne(filter.getProperty(), Integer.valueOf(filter.getValue().toString())));
					;// 此处不用转化为String，因为 <>可以直接判断字符串或数字！
				}
			} else if (filter.getOperator() == Operator.gt && filter.getValue() != null) {
				// restrictions = criteriaBuilder.and(restrictions,
				// criteriaBuilder.gt(root.<Number> get(filter.getProperty()),
				// (Number) filter.getValue()));
				criteria.add(Restrictions.gt(filter.getProperty(), Integer.valueOf(filter.getValue().toString())));
			} else if (filter.getOperator() == Operator.lt && filter.getValue() != null) {
				criteria.add(Restrictions.lt(filter.getProperty(), Integer.valueOf(filter.getValue().toString())));
			} else if (filter.getOperator() == Operator.ge && filter.getValue() != null) {
				criteria.add(Restrictions.ge(filter.getProperty(), Integer.valueOf(filter.getValue().toString())));
			} else if (filter.getOperator() == Operator.le && filter.getValue() != null) {
				criteria.add(Restrictions.le(filter.getProperty(), Integer.valueOf(filter.getValue().toString())));
			} else if (filter.getOperator() == Operator.like && filter.getValue() != null
					&& filter.getValue() instanceof String) {
				// restrictions = criteriaBuilder.and(restrictions,
				// criteriaBuilder.like(root.<String> get(filter.getProperty()),
				// (String) filter.getValue()));
				criteria.add(Restrictions.like(filter.getProperty(), "%" + filter.getValue() + "%"));
			} else if (filter.getOperator() == Operator.in && filter.getValue() != null) {
				// restrictions = criteriaBuilder.and(restrictions,
				// root.get(filter.getProperty()).in(filter.getValue()));
				criteria.add(Restrictions.in(filter.getProperty(), (String[]) filter.getValue()));// 此处必须是数组
			} else if (filter.getOperator() == Operator.isNull) {
				criteria.add(Restrictions.isNull(filter.getProperty()));
			} else if (filter.getOperator() == Operator.isNotNull) {
				criteria.add(Restrictions.isNotNull(filter.getProperty()));
			}
		}
	}

	private void addOrders(Criteria criteria, List<PageOrder> orders) {
		if (criteria == null || orders == null || orders.isEmpty()) {
			return;
		}
		for (PageOrder order : orders) {
			if (order.getDirection() == Direction.asc) {
				criteria.addOrder(org.hibernate.criterion.Order.asc(order.getProperty()));
			} else if (order.getDirection() == Direction.desc) {
				criteria.addOrder(org.hibernate.criterion.Order.desc(order.getProperty()));
			}
		}
		// criteriaQuery.orderBy(orderList);
	}

	@SuppressWarnings("unchecked")
	@Override
	public T find(ID id) {
		Session session = getCurrentSession();
		return (T) session.get(clazz, id);
	}

	@Override
	public T find(ID id, LockModeType lockModeType) {
		return null;
	}

	@Override
	public void persist(T entity) {

	}

	@Override
	public T merge(T entity) {
		return null;
	}

	@Override
	public void remove(T entity) {

	}

	@Override
	public void refresh(T entity) {

	}

	@Override
	public void refresh(T entity, LockModeType lockModeType) {

	}

	@Override
	public ID getIdentifier(T entity) {
		return null;
	}

	@Override
	public boolean isManaged(T entity) {
		return false;
	}

	@Override
	public void detach(T entity) {

	}

	@Override
	public void lock(T entity, LockModeType lockModeType) {

	}

	@Override
	public void clear() {

	}

	@Override
	public void flush() {

	}

	/*
	 * @SuppressWarnings("unchecked") public T get(Serializable id) { return (T)
	 * getCurrentSession().get(clazz, id); }
	 */

	@SuppressWarnings("unchecked")
	@Override
	public List<T> findAll() {
		Criteria criteria = getCurrentSession().createCriteria(clazz);
		return criteria.list();
	}

	@SuppressWarnings("unchecked")
	@Override
	public List<T> query(String hql, Object[] args) {
		Query query = getCurrentSession().createQuery(hql);
		if (args != null) {
			for (int i = 0; i < args.length; i++) {
				query.setParameter(i, args[i]);
			}
		}
		return query.list();
	}

	@Override
	public T getUnique(String hql, Object[] args) {
		if (query(hql, args).size() == 1) // 查找唯一结果
			return query(hql, args).get(0);
		return null;
	}

	/**
	 * 执行带输出参数存储过程
	 * 
	 * @param callSql
	 * @param inParameters
	 * @
	 */
	@Override
	public String callProcedureWithReturn(final String callSql, final Object... inParameters) {
		Session session = getCurrentSession();
		return session.doReturningWork(new ReturningWork<String>() {
			@Override
			public String execute(Connection connection) throws SQLException {
				CallableStatement cs = connection.prepareCall(callSql);
				int inParametersLength = inParameters.length;
				for (int i = 0; i < inParametersLength; i++) {
					cs.setObject(i + 1, inParameters[i]);
				}
				cs.registerOutParameter(inParametersLength + 1, Types.VARCHAR); // 或者用java.sql.Types.VARCHAR
				cs.executeUpdate();
				return cs.getString(inParametersLength + 1);
			}
		});
	}

	/**
	 * 执行只有输入参数存储过程
	 * 
	 * @param callSql
	 *            {call p_datasync(?)}
	 * @param syncTimeType
	 * @
	 */
	@Override
	public void callProcedureNoReturn(String callSql, Object[] params) {
		SQLQuery sqlQuery = getCurrentSession().createSQLQuery(callSql);
		if (null != params) {
			for (int i = 0; i < params.length; i++) {
				sqlQuery.setParameter(i, params[i]);
			}
		}
		sqlQuery.executeUpdate();
	}

}
